{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Stairs Circuit, its gradients evaluated using Rigetti QVM\n",
    "\n",
    "Before reading this Jupyter notebook, \n",
    "we recommend that you first read the earlier notebook \n",
    "named \n",
    "\n",
    ">`Stairs_circuit_and_its_gradients_in_native.ipynb`\n",
    "\n",
    "located in the same folder in the Qubiter repo as this notebook.\n",
    "In that earlier notebook, we use the Qubiter (i.e., native) simulator\n",
    "to evaluate the gradients of a special quantum cost function. In this notebook,\n",
    "we evaluate the same gradients, but, instead of the native simulator, \n",
    "we use the Rigetti QVM (quantum virtual machine), which is a bit more\n",
    "complicated than using the native simulator.\n",
    "\n",
    "Qubiter supports, as a single gate, a general U(2) operation with any number of controls,\n",
    "of either the T (full circle) or F (empty circle) kind. \n",
    "Such gates are fundamental to the Stairs circuit that we are considering.\n",
    "For now at least, Rigetti doesn't\n",
    "support such gates in a single step, so we have figured out a work-around until they do.\n",
    "Programmers are masters at figuring out work-arounds.\n",
    "\n",
    "What we do is use Qubiter's class `CGateExpander` to expand all\n",
    "multi-controlled U(2) gates into simple CNOTs and\n",
    "single qubit rotation gates. This is a very basic set of gates that\n",
    " Rigetti's real and virtual machines can handle.\n",
    "(Also, \n",
    "right before the expansion, we substitute each placeholder\n",
    "variable (aka parameter) by its float value.)\n",
    "But `CGateExpander` takes as input a quantum circuit written in Qubiter's language and \n",
    "returns a new,\n",
    "expanded, quantum circuit also written in Qubiter's language.\n",
    "So further processing is required.\n",
    "We then use Qubiter's class `Qubiter_to_RigettiPyQuil`\n",
    "to translate the expanded quantum circuit from Qubiter's to Rigetti's language.\n",
    "Once we have translated all the quantum circuits\n",
    "to Rigetti's language, we are home free. From there on,\n",
    "we just follow very similar steps to \n",
    "those performed in the earlier, all-native notebook. \n",
    "\n",
    "This notebook demos 2 Qubiter classes:\n",
    "\n",
    "*  `StairsDeriv_rigetti`, evaluates the 4 derivatives of a single gate of a Stairs circuit. It uses Rigetti simulators or their real physical qc to do this. \n",
    "*  `StairsAllDeriv_rigetti`, evaluates all the derivatives, for all the gates of a Stairs circuit. It uses Rigetti simulators or their real physical qc to do this.\n",
    "\n",
    ">This notebook calls Rigetti's method QVMConnection() which only works if you first:\n",
    "* install the Rigetti Forest SDK available at https://www.rigetti.com/forest\n",
    "* open a second terminal (besides the one that runs this notebook) and type \"qvm -S\" in it\n",
    "* open a third terminal and type \"quilc -S\" in it"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "/home/rrtucci/PycharmProjects/qubiter/qubiter/jupyter_notebooks\n",
      "/home/rrtucci/PycharmProjects/qubiter\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import sys\n",
    "print(os.getcwd())\n",
    "os.chdir('../../')\n",
    "print(os.getcwd())\n",
    "sys.path.insert(0,os.getcwd())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyquil.quil import Program\n",
    "from pyquil.api import QVMConnection\n",
    "from pyquil.gates import *\n",
    "from pyquil import get_qc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "qvm_url = \"http://localhost:5000\"\n",
    "compiler_server_address = \"tcp://localhost:5555\"\n",
    "forest_url = \"https://forest-server.qcs.rigetti.com\"\n",
    "qvm = QVMConnection(endpoint=qvm_url, compiler_endpoint=compiler_server_address)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "qc = get_qc('4q-qvm')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# class StairsDeriv_rigetti"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loaded OneQubitGate, WITHOUT autograd.numpy\n"
     ]
    }
   ],
   "source": [
    "from qubiter.adv_applications.StairsDeriv_rigetti import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "    This class is a child of StairsDeriv. Its main purpose is to override\n",
      "    the method get_mean_val() of its abstract parent class StairsDeriv. In\n",
      "    this class, the simulation necessary to evaluate the output of\n",
      "    get_mean_val() is done by Rigetti Pyquil simulators or their physical qc\n",
      "    device.\n",
      "\n",
      "    Attributes\n",
      "    ----------\n",
      "    qc : QuantumComputer\n",
      "        returned by PyQuil method get_qc()\n",
      "    translation_line_list : list[str]\n",
      "        a list of lines of PyQuil code generated by the translator. The\n",
      "        lines all start with \"pg +=\".\n",
      "    translator : Qubiter_to_RigettiPyQuil\n",
      "\n",
      "    \n"
     ]
    }
   ],
   "source": [
    "# print docstring of the class\n",
    "print(StairsDeriv_rigetti.__doc__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('prior', ['#50', '#51', '#52', '#53']),\n",
      "             ('2F', ['#500', '#501', '#502', '#503']),\n",
      "             ('2T', ['#510', '#511', '#512', '#513']),\n",
      "             ('2F1_', ['#5050', '#5051', '#5052', '#5053']),\n",
      "             ('2T1_', ['#5150', '#5151', '#5152', '#5153'])])\n"
     ]
    }
   ],
   "source": [
    "num_qbits = 4\n",
    "parent_num_qbits = num_qbits - 1  # one bit for ancilla\n",
    "\n",
    "# u2_bit_to_higher_bits = None\n",
    "u2_bit_to_higher_bits = {0: [2], 1: [2], 2: []}\n",
    "gate_str_to_rads_list = StairsCkt_writer.\\\n",
    "    get_gate_str_to_rads_list(parent_num_qbits,\n",
    "        '#int', rads_const=np.pi/2,\n",
    "        u2_bit_to_higher_bits=u2_bit_to_higher_bits)\n",
    "pp.pprint(gate_str_to_rads_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2T\n"
     ]
    }
   ],
   "source": [
    "deriv_gate_str = list(gate_str_to_rads_list.keys())[2]\n",
    "print(deriv_gate_str)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "file_prefix = 'stairs_deriv_rigetti_test'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.4 [Y0 Y1] +\n",
      "0.7 [X1 Y2]\n"
     ]
    }
   ],
   "source": [
    "hamil = QubitOperator('X1 Y0 X1 Y1', .4) +\\\n",
    "    QubitOperator('Y2 X1', .7)\n",
    "print(hamil)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "partials_list= [0.0034028451206715993, -0.06295185894191425, -0.05962213592923961, -0.031804538947866376]\n"
     ]
    }
   ],
   "source": [
    "der = StairsDeriv_rigetti(qc, deriv_gate_str,\n",
    "                         gate_str_to_rads_list, file_prefix,\n",
    "                         parent_num_qbits, hamil)\n",
    "\n",
    "var_num_to_rads = StairsCkt_writer.get_var_num_to_rads(\n",
    "    gate_str_to_rads_list, 'const', rads_const=np.pi/2)\n",
    "\n",
    "partials_list = der.get_mean_val(var_num_to_rads)\n",
    "print('partials_list=', partials_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# class StairsAllDeriv_rigetti"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qubiter.adv_applications.StairsAllDeriv_rigetti import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "    This class is a child of StairsDeriv_rigetti. For the parent class,\n",
      "    the get_mean_val() method returns a list of 4 partial derivatives\n",
      "    belonging to a particular gate string (a gate_str is a key in\n",
      "    gate_str_to_rads_list). For this class, get_mean_val() returns an\n",
      "    ordered dictionary mapping each gate_str to its 4 partials.\n",
      "\n",
      "    Attributes\n",
      "    ----------\n",
      "    deriv_gate_str : str\n",
      "\n",
      "    \n"
     ]
    }
   ],
   "source": [
    "# print docstring of the class\n",
    "print(StairsAllDeriv_rigetti.__doc__)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('prior', ['#50', '#51', '#52', '#53']),\n",
      "             ('2F', ['#500', '#501', '#502', '#503']),\n",
      "             ('2T', ['#510', '#511', '#512', '#513']),\n",
      "             ('2F1_', ['#5050', '#5051', '#5052', '#5053']),\n",
      "             ('2T1_', ['#5150', '#5151', '#5152', '#5153'])])\n",
      "OrderedDict([('prior',\n",
      "              [-0.23097482034489736,\n",
      "               -0.08381924385888949,\n",
      "               -0.08381924310178478,\n",
      "               -0.08381924461599415]),\n",
      "             ('2F',\n",
      "              [0.03990238187610799,\n",
      "               0.057359948994478056,\n",
      "               0.07552941989528865,\n",
      "               0.05106475894990702]),\n",
      "             ('2T',\n",
      "              [-0.3022188538314251,\n",
      "               -0.06503835637026129,\n",
      "               -0.07075909489962387,\n",
      "               -0.08659938315985996]),\n",
      "             ('2F1_',\n",
      "              [0.3487952036687368,\n",
      "               0.04268284645027558,\n",
      "               0.089594909274672,\n",
      "               -0.039203811008462916]),\n",
      "             ('2T1_',\n",
      "              [-0.467144918119173,\n",
      "               0.17580028803003597,\n",
      "               0.17338374316874772,\n",
      "               0.1257533545324687])])\n"
     ]
    }
   ],
   "source": [
    "num_qbits = 4\n",
    "parent_num_qbits = num_qbits - 1  # one bit for ancilla\n",
    "\n",
    "# u2_bit_to_higher_bits = None\n",
    "u2_bit_to_higher_bits = {0: [2], 1: [2], 2: []}\n",
    "gate_str_to_rads_list = StairsCkt_writer.\\\n",
    "    get_gate_str_to_rads_list(parent_num_qbits,\n",
    "        '#int', rads_const=np.pi/2,\n",
    "        u2_bit_to_higher_bits=u2_bit_to_higher_bits)\n",
    "pp.pprint(gate_str_to_rads_list)\n",
    "\n",
    "file_prefix = 'stairs_all_deriv_rigetti_test'\n",
    "\n",
    "hamil = QubitOperator('Y0 X1', .4) +\\\n",
    "    QubitOperator('X0', .7)\n",
    "\n",
    "der = StairsAllDeriv_rigetti(qc, gate_str_to_rads_list, file_prefix,\n",
    "                         parent_num_qbits, hamil)\n",
    "\n",
    "var_num_to_rads = StairsCkt_writer.get_var_num_to_rads(\n",
    "    gate_str_to_rads_list, 'const', rads_const=np.pi/2)\n",
    "\n",
    "gate_str_to_partials_list = der.get_mean_val(var_num_to_rads)\n",
    "pp.pprint(gate_str_to_partials_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  },
  "toc": {
   "colors": {
    "hover_highlight": "#DAA520",
    "running_highlight": "#FF0000",
    "selected_highlight": "#FFD700"
   },
   "moveMenuLeft": true,
   "nav_menu": {
    "height": "51px",
    "width": "252px"
   },
   "navigate_menu": true,
   "number_sections": true,
   "sideBar": true,
   "threshold": 4,
   "toc_cell": false,
   "toc_section_display": "block",
   "toc_window_display": false,
   "widenNotebook": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
